home *** CD-ROM | disk | FTP | other *** search
/ Workbench Design / WB Collection.iso / workbench werkzeuge / scherz programme / clicker / source / sound.c < prev    next >
C/C++ Source or Header  |  1996-04-07  |  7KB  |  215 lines

  1. /*  File:         sound.c
  2.  *  Created:      20-10-95
  3.  *  Updated:      30-12-95
  4.  *  Version:      1.0
  5.  *  Project:      Clicker
  6.  *  Owner:        Jeroen Vermeulen
  7.  *  Requirements: KickStart V39+
  8.  *  Legal:        PD
  9.  *  Status:       Release
  10.  */
  11.  
  12. #include <math.h>
  13.  
  14. #include <proto/exec.h>
  15. #include <exec/devices.h>
  16. #include <exec/memory.h>
  17. #include <devices/audio.h>
  18. #include <proto/alib.h>
  19.  
  20. #include "main.h"
  21. #include "sound.h"
  22. #include "prefs.h"
  23.  
  24.  
  25. static STRPTR
  26.         AllocFailChipMem = "Not enough CHIP memory!\n",
  27.         AllocFailPubMem  = "Not enough PUBLIC memory!\n",
  28.         OpenFailAudioDev = "Can't open audio.device\n";
  29.  
  30. /* Here's our basic built-in sample.  It's a very crude sine wave but it should
  31.  * do for those very short intervals.
  32.  * NOTE:  SAMPLELENGTH must be defined to the length of this array!
  33.  */
  34. static signed char Sample[SAMPLELENGTH] = { 0, 60, 100, 127, 100, 60,
  35.                                             0, -60, -100, -127, -100, -60 };
  36.  
  37. /* Allocation priority array for sound channels.  Don't see the point really, as
  38.  * we're only asking for a single lousy sound channel.  Oh well, audio.device
  39.  * seems to insist that we provide this.
  40.  */
  41. static unsigned char SoundChannels[] = { 2,1,4,8 };
  42.  
  43.  
  44. /* CreateSample():
  45.  * Open audio device and sample.  An IOAudio structure is returned unless either
  46.  * an error occurs or the error string was already non-NULL before the call.
  47.  */
  48. struct IOAudio *CreateSample(STRPTR *const error)
  49. {
  50.   struct IOAudio   *soundrequest = NULL;
  51.   struct IORequest *plainrequest;
  52.   struct MsgPort   *Reply;
  53.  
  54.   if ((Reply = CreateMsgPort()))
  55.   {
  56.     plainrequest = CreateIORequest(Reply,sizeof(struct IOAudio));
  57.     soundrequest = (struct IOAudio *)plainrequest;
  58.  
  59.     if (soundrequest)
  60.     {
  61.       soundrequest->ioa_Data = SoundChannels;
  62.       soundrequest->ioa_Length = 4;
  63.       plainrequest->io_Command = ADCMD_ALLOCATE;
  64.       plainrequest->io_Flags = ADIOF_NOWAIT;
  65.  
  66.       if (OpenDevice("audio.device",0,plainrequest,0) == 0)
  67.       {
  68.         UBYTE *SampleMem;
  69.         /* --- */
  70.         SampleMem = (UBYTE *)AllocMem(SAMPLELENGTH,MEMF_PUBLIC|MEMF_CHIP);
  71.  
  72.         if (SampleMem)
  73.         {
  74.           CopyMem(Sample,SampleMem,SAMPLELENGTH);
  75.           plainrequest->io_Flags = ADIOF_PERVOL|IOF_QUICK;
  76.           plainrequest->io_Command = CMD_WRITE;
  77.           soundrequest->ioa_Data = SampleMem;
  78.           soundrequest->ioa_Length = SAMPLELENGTH;
  79.         }
  80.         else *error = AllocFailChipMem;
  81.       }
  82.       else *error = OpenFailAudioDev;
  83.     }
  84.     else *error = AllocFailPubMem;
  85.   }
  86.   else *error = AllocFailMsgPort;
  87.  
  88.   if (*error)
  89.   {
  90.     DeleteSample(soundrequest);
  91.     soundrequest = NULL;
  92.   }
  93.  
  94.   return soundrequest;
  95. }
  96.  
  97.  
  98. /* Destroy sample and free all resources allocated with it.  This function is
  99.  * overly robust so it can be used from within CreateSample() in case of an
  100.  * error.
  101.  */
  102. void DeleteSample(struct IOAudio *const soundrequest)
  103. {
  104.   if (soundrequest)
  105.   {
  106.     struct IORequest *const plainrequest = &soundrequest->ioa_Request;
  107.     if (plainrequest->io_Message.mn_ReplyPort)
  108.     {
  109.       CloseDevice(plainrequest);
  110.       DeleteMsgPort(plainrequest->io_Message.mn_ReplyPort);
  111.     }
  112.     if (soundrequest->ioa_Data) FreeMem(soundrequest->ioa_Data,SAMPLELENGTH);
  113.     DeleteIORequest(plainrequest);
  114.   }
  115. }
  116.  
  117.  
  118. /* KeyClick():
  119.  * Make key-click noise.  The soundrequest pointer is assumed to be valid and
  120.  * non-NULL, and point at a properly initialized IOAudio structure.
  121.  */
  122. void KeyClick(struct IOAudio *const soundrequest)
  123. {
  124.   if (ClickPrefs.newsettings)
  125.   {
  126.     ClickPrefs.newsettings = FALSE;
  127.     soundrequest->ioa_Period = ClickPrefs.period;
  128.     soundrequest->ioa_Volume = ClickPrefs.volume;
  129.     soundrequest->ioa_Cycles = ClickPrefs.cycles;
  130.     soundrequest->ioa_Request.io_Flags = ADIOF_PERVOL | IOF_QUICK;
  131.   }
  132.   BeginIO(&soundrequest->ioa_Request);
  133.   WaitIO(&soundrequest->ioa_Request);
  134. }
  135.  
  136.  
  137. /* SliderToHertz():
  138.  * Converts a prefs window slider position (between -5*12 and 4*12) to a
  139.  * frequency in Hertz, based on a twelve-tone octave centered at the 440 Hz A.
  140.  */
  141. LONG SliderToHertz(const struct Gadget *const dum, const WORD sliderpos)
  142. {
  143.   /* Frequency is 440 * 2^(n/12).  Note that left-shift cannot be used here.
  144.    */
  145.   return (LONG)(440 * pow((double)2,sliderpos/(double)12));
  146. }
  147.  
  148.  
  149. /* HertzToPeriod():
  150.  * Converts human-readable pitch in Hertz to period length suitable for use by
  151.  * audio.device.  As a rule, HertzToPeriod(SliderToHertz(S)) is equivalent to
  152.  * SliderToPeriod(S).
  153.  */
  154. UWORD HertzToPeriod(const LONG Hertz)
  155. {
  156.   /* Period should be computed from frequency f as 10^9 / (279.365 * s * f),
  157.    * where s is the sample length.  With proper constant folding, this should
  158.    * compile to (C/Hertz) where C is a constant.
  159.    */
  160.   return (UWORD)((1000000000/(279.365*SAMPLELENGTH))/Hertz);
  161. }
  162.  
  163.  
  164. /* PeriodToHertz():
  165.  * Converts audio.device period length to human-readable pitch in Hertz.  This
  166.  * is the inverse of HertzToPeriod().
  167.  */
  168. LONG PeriodToHertz(const UWORD period)
  169. {
  170.   /* Pitch in Hertz is computed from period f as 279.365 * s * p / 10^9, where s
  171.    * is sample length.
  172.    */
  173.   return (LONG)((279.365 * SAMPLELENGTH / 1000000000) * period);
  174. }
  175.  
  176.  
  177. /* SliderToPeriod():
  178.  * Converts a prefs window slider position (between 0 and 9*12) to a period
  179.  * length (in units of 279.365 nanoseconds) suitable for use by audio.device.
  180.  */
  181. UWORD SliderToPeriod(const WORD sliderpos)
  182. {
  183.   /* For a frequency f, the period length is 10^9 / (279.365 * s * f).  Here s
  184.    * is the length of the waveform sample (SAMPLELENGTH).
  185.    * So we need to compute 10^9 / (279.365 * s * (440 * 2^(n/12)))
  186.    * == 2^(-n/12) * 10^9 / (279.365 * 440 * s)
  187.    * ==  2^(-n/12) * 10^9 / (122920.6 * s)
  188.    */
  189.   return (UWORD)(pow((double)2,-sliderpos/(double)12) *
  190.                 ((double)10000000 / (1229.206 * SAMPLELENGTH)));
  191. }
  192.  
  193.  
  194. /* PeriodToSlider():
  195.  * Inverse of SliderToPeriod().  Takes a period length as an argument and
  196.  * computes the appropriate slider position (between -5*12 and 4*12).  This
  197.  * function can afford to be slow because it's only ever called when the prefs
  198.  * window pops up.
  199.  */
  200. WORD PeriodToSlider(const UWORD period)
  201. {
  202.   /* Since the period number p can be computed from slider position n by
  203.    *  p = 10^9 / (279.365 * 440 * s * 2^(n/12))  <==>
  204.    *  2^(n/12) = 10^9 / (279.365 * 440 * s * p)  <==>
  205.    *  n/12 = 2log(10^9 / (279.365 * 440 * s * p)) <==>
  206.    *  n = 12 * 2log(10^9 / (279.365 * 440 * s * p))  ==
  207.    *      12 * 2log(10^9 / (122920.6 * s * p))  ==
  208.    *      12 * ln(10^9 /  (122920.6 * s * p)) / ln(2)
  209.    */
  210.   return (WORD)(12 * log(
  211.                 (double)1000000000 / (122920.6*SAMPLELENGTH*period)
  212.                )/log((double)2));
  213. }
  214.  
  215.